home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue61 / Alfresco / AAPriQue.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  2000-08-08  |  15.8 KB  |  518 lines

  1. {*********************************************************}
  2. {* AAPriQue                                              *}
  3. {* Copyright (c) Julian M Bucknall 1998                  *}
  4. {* All rights reserved.                                  *}
  5. {*********************************************************}
  6. {* Priority queues                                       *}
  7. {*********************************************************}
  8.  
  9. {Note: this unit is released as freeware. In other words, you are free
  10.        to use this unit in your own applications, however I retain all
  11.        copyright to the code. JMB}
  12.  
  13. unit AAPriQue;
  14.  
  15. interface
  16.  
  17. uses
  18.   SysUtils,
  19.   Classes;
  20.  
  21. type
  22.   TaaItemPriorityCompare = function(const aItem1, aItem2 : pointer) : integer;
  23.     {-Function prototype to take two items and compare their
  24.       priorities: returns < 0 if the first item's priority is less
  25.       than the second's, 0 if they're equal, > 0 otherwise}
  26.  
  27. type
  28.   TaaPriorityQueue = class
  29.     {-A priority queue that uses the heap algorithm}
  30.     private
  31.       pqCompare      : TaaItemPriorityCompare;
  32.       pqExternalList : boolean;
  33.       pqList         : TList;
  34.     protected
  35.       function pqGetCount : integer;
  36.  
  37.       procedure pqBubbleUp(aFromInx : integer; aItem : pointer);
  38.       procedure pqMakeIntoHeap;
  39.       procedure pqTrickleDown(aFromInx : integer; aItem : pointer);
  40.     public
  41.       constructor Create(aCompareFn : TaaItemPriorityCompare);
  42.         {-Create the priority queue}
  43.       constructor CreateWithList(aCompareFn : TaaItemPriorityCompare;
  44.                                  aList      : TList);
  45.         {-Create the priority queue with an existing list}
  46.       destructor Destroy; override;
  47.         {-Dispose of the priority queue - items remaining are NOT
  48.           freed}
  49.  
  50.       procedure Add(aItem : pointer);
  51.         {-Add an item to the priority queue}
  52.       function Remove : pointer;
  53.         {-Remove and return the item with the largest priority}
  54.  
  55.       property Count : integer read pqGetCount;
  56.         {-Count of items in the queue}
  57.  
  58.  
  59.       property List : TList read pqList;
  60.   end;
  61.  
  62. type
  63.   TaaPQHandle = pointer;
  64.  
  65.   TaaPriorityQueueEx = class
  66.     {-A priority queue that uses the heap algorithm and that allows
  67.       deletion and reprioritisation of arbitrary items}
  68.     private
  69.       pqCompare : TaaItemPriorityCompare;
  70.       pqHandles : pointer;
  71.       pqList    : TList;
  72.     protected
  73.       function pqGetCount : integer;
  74.  
  75.       procedure pqBubbleUp(aFromInx : integer; aHandle : pointer);
  76.       procedure pqTrickleDown(aFromInx : integer; aHandle : pointer);
  77.  
  78.       {$IFOPT D+}
  79.       procedure VerifyIndirection;
  80.       {$ENDIF}
  81.  
  82.     public
  83.       constructor Create(aCompareFn : TaaItemPriorityCompare);
  84.         {-Create the priority queue}
  85.       destructor Destroy; override;
  86.         {-Dispose of the priority queue - items remaining are NOT
  87.           freed}
  88.  
  89.       function Add(aItem : pointer) : TaaPQHandle;
  90.         {-Add an item to the priority queue; return handle}
  91.       procedure Delete(var aHandle : TaaPQHandle);
  92.         {-Delete an item referenced by its handle from the priority
  93.           queue; the handle is set to nil on return}
  94.       function Peek : pointer;
  95.         {-Peek at the top item}
  96.       function Remove : pointer;
  97.         {-Remove and return the item with the largest priority}
  98.       procedure Replace(aHandle : TaaPQHandle; aItem : pointer);
  99.         {-Replace the item referenced by the handle in the priority
  100.           queue}
  101.  
  102.       property Count : integer read pqGetCount;
  103.         {-Count of items in the queue}
  104.  
  105.       property List : TList read pqList;
  106.   end;
  107.  
  108. implementation
  109.  
  110.  
  111. {===TaaPriorityQueue=================================================}
  112. constructor TaaPriorityQueue.Create(aCompareFn : TaaItemPriorityCompare);
  113. begin
  114.   inherited Create;
  115.   pqCompare := aCompareFn;
  116.   pqList := TList.Create;
  117. end;
  118. {--------}
  119. constructor TaaPriorityQueue.CreateWithList(aCompareFn : TaaItemPriorityCompare;
  120.                                             aList      : TList);
  121. begin
  122.   inherited Create;
  123.   pqCompare := aCompareFn;
  124.   pqList := aList;
  125.   pqExternalList := true;
  126.   pqMakeIntoHeap;
  127. end;
  128. {--------}
  129. destructor TaaPriorityQueue.Destroy;
  130. begin
  131.   if not pqExternalList then
  132.     pqList.Free;
  133.   inherited Destroy;
  134. end;
  135. {--------}
  136. procedure TaaPriorityQueue.Add(aItem : pointer);
  137. begin
  138.   {add extra space at the end of the queue}
  139.   pqList.Count := pqList.Count + 1;
  140.   {now bubble it up as far as it will go}
  141.   pqBubbleUp(pred(pqList.Count), aItem);
  142. end;
  143. {--------}
  144. procedure TaaPriorityQueue.pqBubbleUp(aFromInx : integer; aItem : pointer);
  145. var
  146.   ParentInx : integer;
  147. begin
  148.   {while the item under consideration is larger than its parent, swap
  149.    it with its parent and continue from its new position}
  150.   {Note: the parent for the child at index N is at (N-1) div 2}
  151.   ParentInx := (aFromInx - 1) div 2;
  152.   {while our item has a parent, and it's greater than the parent...}
  153.   while (aFromInx > 0) and
  154.         (pqCompare(aItem, pqList[ParentInx]) > 0) do begin
  155.     {move our parent down the tree}
  156.     pqList[aFromInx] := pqList[ParentInx];
  157.     aFromInx := ParentInx;
  158.     ParentInx := (aFromInx - 1) div 2;
  159.   end;
  160.   {store our item in the correct place}
  161.   pqList[aFromInx] := aItem;
  162. end;
  163. {--------}
  164. function TaaPriorityQueue.pqGetCount : integer;
  165. begin
  166.   Result := pqList.Count;
  167. end;
  168. {--------}
  169. procedure TaaPriorityQueue.pqMakeIntoHeap;
  170. var
  171.   Inx : integer;
  172. begin
  173.   {starting from the lowest, rightmost parent, trickle down and then
  174.    continue with the rest of the parents from right to left, bottom to
  175.    top. The rightmost parent is the parent of the last item. This is
  176.    ((count-1)-1) div 2}
  177.   for Inx := ((pqList.Count - 2) div 2) downto 0 do 
  178.     pqTrickleDown(Inx, pqList[Inx]);
  179. end;
  180. {--------}
  181. procedure TaaPriorityQueue.pqTrickleDown(aFromInx : integer; aItem : pointer);
  182. var
  183.   ChildInx  : integer;
  184.   ListCount : integer;
  185. begin
  186.   {while the item under consideration is smaller than one of its
  187.    children, swap it with the larger child and continue from its new
  188.    position}
  189.   {Note: the children for the parent at index N are at (2N+1) and
  190.          2N+2}
  191.   ListCount := pqList.Count;
  192.   {calculate the left child index}
  193.   ChildInx := succ(aFromInx * 2);
  194.   {while there is at least a left child...}
  195.   while (ChildInx < ListCount) do begin
  196.     {if there is a right child, calculate the index of the larger
  197.      child}
  198.     if (succ(ChildInx) < ListCount) and
  199.        (pqCompare(pqList[ChildInx], pqList[succ(ChildInx)]) < 0) then
  200.       inc(ChildInx);
  201.     {if our item is greater or equal to the larger child, we're done}
  202.     if (pqCompare(aItem, pqList[ChildInx]) >= 0) then
  203.       Break;
  204.     {otherwise move the larger child up the tree, and move our item
  205.      down the tree and repeat}
  206.     pqList[aFromInx] := pqList[ChildInx];
  207.     aFromInx := ChildInx;
  208.     ChildInx := succ(aFromInx * 2);
  209.   end;
  210.   {store our item in the correct place}
  211.   pqList[aFromInx] := aItem;
  212. end;
  213. {--------}
  214. function TaaPriorityQueue.Remove : pointer;
  215. begin
  216.   {return the item at the root}
  217.   Result := pqList[0];
  218.   {replace the root with the child at the lowest, rightmost position,
  219.    and shrink the list}
  220.   pqList[0] := pqList.Last;
  221.   pqList.Count := pqList.Count - 1;
  222.   {now trickle down the root item as far as it will go}
  223.   if (pqList.Count > 0) then
  224.     pqTrickleDown(0, pqList[0]);
  225. end;
  226. {====================================================================}
  227.  
  228.  
  229. {===Linked list helper routines======================================}
  230. type
  231.   PllNode = ^TllNode;
  232.   TllNode = packed record
  233.     lliNext : PllNode;
  234.     lliPrev : PllNode;
  235.     lliItem : pointer;
  236.     lliInx  : integer;
  237.   end;
  238. {--------}
  239. function CreateLinkedList : PllNode;
  240. begin
  241.   Result := AllocMem(sizeof(TllNode));
  242.   Result^.lliNext := AllocMem(sizeof(TllNode));
  243.   Result^.lliNext^.lliPrev := Result;
  244. end;
  245. {--------}
  246. procedure DestroyLinkedList(aLinkedList : PllNode);
  247. var
  248.   Temp : PllNode;
  249. begin
  250.   while (aLinkedList <> nil) do begin
  251.     Temp := aLinkedList;
  252.     aLinkedList := aLinkedList^.lliNext;
  253.     FreeMem(Temp, sizeof(TllNode));
  254.   end;
  255. end;
  256. {--------}
  257. function AddLinkedListNode(aLinkedList : PllNode; aItem : pointer) : PllNode;
  258. begin
  259.   Result := AllocMem(sizeof(TllNode));
  260. //  writeln(format('add: %p', [Result]));
  261.   Result^.lliNext := aLinkedList^.lliNext;
  262.   Result^.lliPrev := aLinkedList;
  263.   aLinkedList^.lliNext^.lliPrev := Result;
  264.   aLinkedList^.lliNext := Result;
  265.   Result^.lliItem := aItem;
  266. end;
  267. {--------}
  268. procedure DeleteLinkedListNode(aLinkedList : PllNode; aNode : PllNode);
  269. begin
  270.   aNode^.lliPrev^.lliNext := aNode^.lliNext;
  271.   aNode^.lliNext^.lliPrev := aNode^.lliPrev;
  272. //  writeln(format('del: %p', [aNode]));
  273.   FreeMem(aNode, sizeof(TllNode));
  274. end;
  275. {====================================================================}
  276.  
  277.  
  278. {===TaaPriorityQueueEx===============================================}
  279. constructor TaaPriorityQueueEx.Create(aCompareFn : TaaItemPriorityCompare);
  280. begin
  281.   inherited Create;
  282.   pqCompare := aCompareFn;
  283.   pqList := TList.Create;
  284.   pqHandles := CreateLinkedList;
  285. end;
  286. {--------}
  287. destructor TaaPriorityQueueEx.Destroy;
  288. begin
  289.   pqList.Free;
  290.   DestroyLinkedList(pqHandles);
  291.   inherited Destroy;
  292. end;
  293. {--------}
  294. function TaaPriorityQueueEx.Add(aItem : pointer) : TaaPQHandle;
  295. var
  296.   Handle : PllNode;
  297. begin
  298.   {add extra space at the end of the queue}
  299.   pqList.Count := pqList.Count + 1;
  300.   {create a new node for the linked list}
  301.   Handle := AddLinkedListNode(pqHandles, aItem);
  302.   {now bubble it up as far as it will go}
  303.   if (pqList.Count = 1) then begin
  304.     pqList[0] := Handle;
  305.     Handle^.lliInx := 0;
  306.   end
  307.   else
  308.     pqBubbleUp(pred(pqList.Count), Handle);
  309.   {return the handle}
  310.   Result := Handle;
  311.   {$IFOPT D+}
  312.   VerifyIndirection;
  313.   {$ENDIF}
  314. end;
  315. {--------}
  316. procedure TaaPriorityQueueEx.Delete(var aHandle : TaaPQHandle);
  317. var
  318.   Handle    : PllNode absolute aHandle;
  319.   NewHandle : PllNode;
  320.   HeapInx   : integer;
  321.   ParentInx    : integer;
  322.   ParentHandle : PllNode;
  323. begin
  324.   {delete the handle}
  325.   HeapInx := Handle^.lliInx;
  326.   DeleteLinkedListNode(pqHandles, Handle);
  327.   Handle := nil;
  328.   {check to see whether we deleted the last item, if so just shrink
  329.    the heap - the heap property will still apply}
  330.   if (HeapInx = pred(pqList.Count)) then
  331.     pqList.Count := pqList.Count - 1
  332.   else begin
  333.     {replace the heap element with the child at the lowest, rightmost
  334.      position, and shrink the list}
  335.     NewHandle := pqList.Last;
  336.     pqList[HeapInx] := NewHandle;
  337.     NewHandle^.lliInx := HeapInx;
  338.     pqList.Count := pqList.Count - 1;
  339.     {check to see whether we can bubble up}
  340.     if (HeapInx > 0) then begin
  341.       ParentInx := (HeapInx - 1) div 2;
  342.       ParentHandle := PllNode(pqList[ParentInx]);
  343.       if (pqCompare(NewHandle^.lliItem, ParentHandle^.lliItem) > 0) then begin
  344.         pqBubbleUp(HeapInx, NewHandle);
  345.         {$IFOPT D+}
  346.         VerifyIndirection;
  347.         {$ENDIF}
  348.         Exit;
  349.       end;
  350.     end;
  351.     {otherwise trickle down}
  352.     if (pqList.Count > 0) then
  353.       pqTrickleDown(HeapInx, pqList[HeapInx]);
  354.   end;
  355.   {$IFOPT D+}
  356.   VerifyIndirection;
  357.   {$ENDIF}
  358. end;
  359. {--------}
  360. function TaaPriorityQueueEx.Peek : pointer;
  361. var
  362.   Node : PllNode;
  363. begin
  364.   if (Count > 0) then begin
  365.     Node := pqList[0];
  366.     Result := Node^.lliItem;
  367.   end
  368.   else
  369.     Result := nil;
  370. end;
  371. {--------}
  372. procedure TaaPriorityQueueEx.pqBubbleUp(aFromInx : integer; aHandle : pointer);
  373. var
  374.   ParentInx    : integer;
  375.   ParentHandle : PllNode;
  376.   Handle       : PllNode absolute aHandle;
  377. begin
  378.   {while the handle under consideration is larger than its parent,
  379.    swap it with its parent and continue from its new position}
  380.   {Note: the parent for the child at index N is at (N-1) div 2}
  381.   if (aFromInx > 0) then begin
  382.     ParentInx := (aFromInx - 1) div 2;
  383.     ParentHandle := PllNode(pqList[ParentInx]);
  384.     {while our item has a parent, and it's greater than the parent...}
  385.     while (aFromInx > 0) and
  386.           (pqCompare(Handle^.lliItem, ParentHandle^.lliItem) > 0) do begin
  387.       {move our parent down the tree}
  388.       pqList[aFromInx] := ParentHandle;
  389.       ParentHandle^.lliInx := aFromInx;
  390.       aFromInx := ParentInx;
  391.       ParentInx := (aFromInx - 1) div 2;
  392.       ParentHandle := PllNode(pqList[ParentInx]);
  393.     end;
  394.   end;
  395.   {store our item in the correct place}
  396.   pqList[aFromInx] := Handle;
  397.   Handle^.lliInx := aFromInx;
  398. end;
  399. {--------}
  400. function TaaPriorityQueueEx.pqGetCount : integer;
  401. begin
  402.   Result := pqList.Count;
  403. end;
  404. {--------}
  405. procedure TaaPriorityQueueEx.pqTrickleDown(aFromInx : integer; aHandle : pointer);
  406. var
  407.   ListCount   : integer;
  408.   ChildInx    : integer;
  409.   ChildHandle : PllNode;
  410.   Handle      : PllNode absolute aHandle;
  411. begin
  412.   {while the item under consideration is smaller than one of its
  413.    children, swap it with the larger child and continue from its new
  414.    position}
  415.   {Note: the children for the parent at index N are at (2N+1) and
  416.          2N+2}
  417.   ListCount := pqList.Count;
  418.   {calculate the left child index}
  419.   ChildInx := succ(aFromInx * 2);
  420.   {while there is at least a left child...}
  421.   while (ChildInx < ListCount) do begin
  422.     {if there is a right child, calculate the index of the larger
  423.      child}
  424.     if (succ(ChildInx) < ListCount) and
  425.        (pqCompare(PllNode(pqList[ChildInx])^.lliItem,
  426.                   PllNode(pqList[succ(ChildInx)])^.lliItem) < 0) then
  427.       inc(ChildInx);
  428.     {if our item is greater or equal to the larger child, we're done}
  429.     ChildHandle := PllNode(pqList[ChildInx]);
  430.     if (pqCompare(Handle^.lliItem, ChildHandle^.lliItem) >= 0) then
  431.       Break;
  432.     {otherwise move the larger child up the tree, and move our item
  433.      down the tree and repeat}
  434.     pqList[aFromInx] := ChildHandle;
  435.     ChildHandle^.lliInx := aFromInx;
  436.     aFromInx := ChildInx;
  437.     ChildInx := succ(aFromInx * 2);
  438.   end;
  439.   {store our item in the correct place}
  440.   pqList[aFromInx] := Handle;
  441.   Handle^.lliInx := aFromInx;
  442. end;
  443. {--------}
  444. function TaaPriorityQueueEx.Remove : pointer;
  445. var
  446.   Handle : PllNode;
  447. begin
  448.   {return the item at the root}
  449.   Handle := pqList[0];
  450.   Result := Handle^.lliItem;
  451.   DeleteLinkedListNode(pqHandles, Handle);
  452.   {if we've just removed the final node, just set the count to zero}
  453.   if (pqList.Count = 1) then begin
  454.     pqList.Count := 0;
  455.   end
  456.   {otherwise, replace the root with the child at the lowest, rightmost
  457.    position, and shrink the list}
  458.   else begin
  459.     Handle := pqList.Last;
  460.     pqList[0] := Handle;
  461.     Handle^.lliInx := 0;
  462.     pqList.Count := pqList.Count - 1;
  463.     {now trickle down the root item as far as it will go}
  464.     pqTrickleDown(0, Handle);
  465.   end;
  466.   {$IFOPT D+}
  467.   VerifyIndirection;
  468.   {$ENDIF}
  469. end;
  470. {--------}
  471. procedure TaaPriorityQueueEx.Replace(aHandle : TaaPQHandle; aItem : pointer);
  472. var
  473.   Handle : PllNode absolute aHandle;
  474.   ParentInx    : integer;
  475.   ParentHandle : PllNode;
  476. begin
  477.   {first, replace the item}
  478.   Handle^.lliItem := aItem;
  479.   {check to see whether we can bubble up}
  480.   if (Handle^.lliInx > 0) then begin
  481.     ParentInx := (Handle^.lliInx - 1) div 2;
  482.     ParentHandle := PllNode(pqList[ParentInx]);
  483.     if (pqCompare(Handle^.lliItem, ParentHandle^.lliItem) > 0) then begin
  484.       pqBubbleUp(Handle^.lliInx, Handle);
  485.       {$IFOPT D+}
  486.       VerifyIndirection;
  487.       {$ENDIF}
  488.       Exit;
  489.     end;
  490.   end;
  491.   {otherwise trickle down}
  492.   pqTrickleDown(Handle^.lliInx, Handle);
  493.   {$IFOPT D+}
  494.   VerifyIndirection;
  495.   {$ENDIF}
  496. end;
  497. {--------}
  498. {$IFOPT D+}
  499. procedure TaaPriorityQueueEx.VerifyIndirection;
  500. type
  501.   Plongint = ^longint;
  502. var
  503.   i : integer;
  504.   Handle : PllNode;
  505. begin
  506.   for i := 0 to pred(pqList.Count) do begin
  507.     Handle := PllNode(pqList[i]);
  508.     if (Handle^.lliInx <> i) then begin
  509.       writeln('ERROR: Handle at ', i, ' doesn''t point to it');
  510.       readln;
  511.     end;
  512.   end;
  513. end;
  514. {$ENDIF}
  515. {====================================================================}
  516.  
  517. end.
  518.